﻿
namespace Anycmd.Host
{
    using AC;
    using Anycmd.AC;
    using Repositories;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Util;

    /// <summary>
    /// 表示账户权限类型。账户权限是主体为账户客体为其它AC元素的权限记录。
    /// </summary>
    public sealed class AccountPrivilege
    {
        private bool _initialized = false;
        private readonly Guid accountID;
        private readonly IACDomain host;
        private HashSet<Guid> authorizedRoleIDs;
        private List<RoleState> authorizedRoles;
        private HashSet<MenuState> authorizedMenus;
        private HashSet<Guid> authorizedFunctionIDs;
        private List<FunctionState> authorizedFunctions;
        private List<PrivilegeBigramState> accountPrivileges;

        private HashSet<OrganizationState> organizations;
        private HashSet<RoleState> roles;
        private HashSet<GroupState> groups;
        private HashSet<FunctionState> functions;
        private HashSet<MenuState> menus;
        private HashSet<AppSystemState> appSystems;

        public AccountPrivilege(IACDomain host, Guid accountID)
        {
            this.host = host;
            this.accountID = accountID;
        }

        public HashSet<OrganizationState> Organizations
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return organizations;
            }
        }
        public HashSet<RoleState> Roles
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return roles;
            }
        }
        public HashSet<GroupState> Groups
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return groups;
            }
        }
        public HashSet<FunctionState> Functions
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return functions;
            }
        }
        public HashSet<MenuState> Menus
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return menus;
            }
        }
        public HashSet<AppSystemState> AppSystems
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                return appSystems;
            }
        }

        public IReadOnlyCollection<PrivilegeBigramState> AccountPrivileges
        {
            get
            {
                if (accountPrivileges == null)
                {
                    if (accountID == Guid.Empty)
                    {
                        return new List<PrivilegeBigramState>();
                    }
                    accountPrivileges = GetAccountPrivileges();
                }
                return accountPrivileges;
            }
        }

        private List<PrivilegeBigramState> GetAccountPrivileges()
        {
            var subjectType = ACSubjectType.Account.ToName();
            var accountPrivileges = host.GetRequiredService<IRepository<PrivilegeBigram>>().AsQueryable()
                .Where(a => a.SubjectType == subjectType && a.SubjectInstanceID == accountID).ToList().Select(a => PrivilegeBigramState.Create(a)).ToList();
            return accountPrivileges;
        }

        #region AddActiveRole
        /// <summary>
        /// 添加激活角色。
        /// </summary>
        /// <param name="user"></param>
        /// <param name="role"></param>
        public void AddActiveRole(RoleState role)
        {
            if (!_initialized)
            {
                Init();
            }
            if (this.Roles != null)
            {
                this.Roles.Add(role);
            }
            if (this.authorizedRoles != null)
            {
                this.AuthorizedRoleIDs.Add(role.Id);
                this.authorizedRoles.Add(role);
            }
        }
        #endregion

        #region DropActiveRole
        /// <summary>
        /// 删除激活角色。
        /// </summary>
        /// <param name="user"></param>
        /// <param name="role"></param>
        public void DropActiveRole(RoleState role)
        {
            if (!_initialized)
            {
                Init();
            }
            if (this.Roles != null)
            {
                this.Roles.Remove(role);
            }
            if (this.authorizedRoles != null)
            {
                this.AuthorizedRoleIDs.Remove(role.Id);
                this.authorizedRoles.Remove(role);
            }
        }
        #endregion

        /// <summary>
        /// 账户的角色授权
        /// 这些角色是以下角色集合的并集：
        /// 1，当前账户直接得到的角色；
        /// 2，当前账户所在的工作组的角色；
        /// 3，当前账户所在的组织结构的角色；
        /// 4，当前账户所在的组织结构加入的工作组的角色。
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public HashSet<Guid> AuthorizedRoleIDs
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                if (authorizedRoleIDs == null)
                {
                    authorizedRoleIDs = new HashSet<Guid>();
                    foreach (var role in this.Roles)
                    {
                        authorizedRoleIDs.Add(role.Id);
                    }
                    foreach (var organization in this.Organizations)
                    {
                        foreach (var item in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Organization && a.SubjectInstanceID == organization.Id))
                        {
                            if (item.ObjectType == ACObjectType.Role)
                            {
                                RoleState role;
                                if (host.RoleSet.TryGetRole(item.ObjectInstanceID, out role))
                                {
                                    authorizedRoleIDs.Add(role.Id);
                                }
                            }
                            else if (item.ObjectType == ACObjectType.Group)
                            {
                                foreach (var roleGroup in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.ObjectType == ACObjectType.Group && a.ObjectInstanceID == item.ObjectInstanceID))
                                {
                                    RoleState role;
                                    if (host.RoleSet.TryGetRole(roleGroup.SubjectInstanceID, out role))
                                    {
                                        authorizedRoleIDs.Add(role.Id);
                                    }
                                }
                            }
                        }
                    }
                    foreach (var group in this.Groups)
                    {
                        foreach (var roleGroup in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.ObjectType == ACObjectType.Group && a.ObjectInstanceID == group.Id))
                        {
                            RoleState role;
                            if (host.RoleSet.TryGetRole(roleGroup.SubjectInstanceID, out role))
                            {
                                authorizedRoleIDs.Add(role.Id);
                            }
                        }
                    }
                }

                return authorizedRoleIDs;
            }
        }

        public IReadOnlyCollection<RoleState> AuthorizedRoles
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                if (authorizedRoles == null)
                {
                    authorizedRoles = new List<RoleState>();
                    foreach (var roleID in this.AuthorizedRoleIDs)
                    {
                        RoleState role;
                        if (this.host.RoleSet.TryGetRole(roleID, out role))
                        {
                            authorizedRoles.Add(role);
                        }
                    }
                }
                return authorizedRoles;
            }
        }

        public HashSet<MenuState> AuthorizedMenus
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                if (authorizedMenus == null)
                {
                    authorizedMenus = new HashSet<MenuState>();
                    List<MenuState> menuList = new List<MenuState>();
                    if (host.UserSession.IsDeveloper())
                    {
                        foreach (var menu in host.MenuSet)
                        {
                            menuList.Add(menu);
                        }
                    }
                    else
                    {
                        var roleIDs = new HashSet<Guid>();
                        foreach (var roleID in this.AuthorizedRoleIDs)
                        {
                            roleIDs.Add(roleID);
                        }
                        foreach (var roleMenu in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.ObjectType == ACObjectType.Menu && roleIDs.Contains(a.SubjectInstanceID)))
                        {
                            MenuState menu;
                            if (host.MenuSet.TryGetMenu(roleMenu.ObjectInstanceID, out menu))
                            {
                                menuList.Add(menu);
                            }
                        }
                        foreach (var menu in this.Menus)
                        {
                            menuList.Add(menu);
                        }
                    }
                    foreach (var menu in menuList.OrderBy(a => a.SortCode))
                    {
                        authorizedMenus.Add(menu);
                    }
                }
                return authorizedMenus;
            }
        }

        public HashSet<Guid> AuthorizedFunctionIDs
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                if (authorizedFunctionIDs == null)
                {
                    authorizedFunctionIDs = new HashSet<Guid>();
                    // TODO:考虑在PrivilegeSet集合中计算好缓存起来，从而可以直接根据角色索引而
                    var roleIDs = this.AuthorizedRoleIDs;
                    foreach (var privilegeBigram in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Role && a.ObjectType == ACObjectType.Function && roleIDs.Contains(a.SubjectInstanceID)))
                    {
                        authorizedFunctionIDs.Add(privilegeBigram.ObjectInstanceID);
                    }
                    // 追加账户所在组织结构的直接功能授权
                    foreach (var organization in this.Organizations)
                    {
                        foreach (var item in host.PrivilegeSet.Where(a => a.SubjectType == ACSubjectType.Organization && a.ObjectType == ACObjectType.Function && a.SubjectInstanceID == organization.Id))
                        {
                            Guid functionID = item.ObjectInstanceID;
                            authorizedFunctionIDs.Add(functionID);
                        }
                    }
                    // 追加账户的直接功能授权
                    foreach (var fun in this.Functions)
                    {
                        authorizedFunctionIDs.Add(fun.Id);
                    }
                }

                return authorizedFunctionIDs;
            }
        }

        public IReadOnlyCollection<FunctionState> AuthorizedFunctions
        {
            get
            {
                if (!_initialized)
                {
                    Init();
                }
                if (authorizedFunctions == null)
                {
                    authorizedFunctions = new List<FunctionState>();
                    foreach (var functionID in this.AuthorizedFunctionIDs)
                    {
                        FunctionState function;
                        if (host.FunctionSet.TryGetFunction(functionID, out function))
                        {
                            authorizedFunctions.Add(function);
                        }
                    }
                }
                return authorizedFunctions;
            }
        }

        private void Init()
        {
            if (!_initialized)
            {
                var organizations = new HashSet<OrganizationState>();
                var roles = new HashSet<RoleState>();
                var groups = new HashSet<GroupState>();
                var functions = new HashSet<FunctionState>();
                var menus = new HashSet<MenuState>();
                var appSystems = new HashSet<AppSystemState>();
                foreach (var accountPrivilege in this.AccountPrivileges)
                {
                    switch (accountPrivilege.ObjectType)
                    {
                        case ACObjectType.Undefined:
                            break;
                        case ACObjectType.Account:
                            break;
                        case ACObjectType.Organization:
                            {
                                Guid organizationID = accountPrivilege.ObjectInstanceID;
                                OrganizationState organization;
                                if (host.OrganizationSet.TryGetOrganization(organizationID, out organization))
                                {
                                    organizations.Add(organization);
                                }
                                break;
                            }
                        case ACObjectType.Role:
                            {
                                Guid roleID = accountPrivilege.ObjectInstanceID;
                                RoleState role;
                                if (host.RoleSet.TryGetRole(roleID, out role))
                                {
                                    roles.Add(role);
                                }
                                break;
                            }
                        case ACObjectType.Group:
                            {
                                Guid groupID = accountPrivilege.ObjectInstanceID;
                                GroupState group;
                                if (host.GroupSet.TryGetGroup(groupID, out group))
                                {
                                    groups.Add(group);
                                }
                                break;
                            }
                        case ACObjectType.Function:
                            {
                                Guid functionID = accountPrivilege.ObjectInstanceID;
                                FunctionState function;
                                if (host.FunctionSet.TryGetFunction(functionID, out function))
                                {
                                    functions.Add(function);
                                }
                                break;
                            }
                        case ACObjectType.Menu:
                            {
                                Guid menuID = accountPrivilege.ObjectInstanceID;
                                MenuState menu;
                                if (host.MenuSet.TryGetMenu(menuID, out menu))
                                {
                                    menus.Add(menu);
                                }
                                break;
                            }
                        case ACObjectType.AppSystem:
                            {
                                Guid appSystemID = accountPrivilege.ObjectInstanceID;
                                AppSystemState appSystem;
                                if (host.AppSystemSet.TryGetAppSystem(appSystemID, out appSystem))
                                {
                                    appSystems.Add(appSystem);
                                }
                                break;
                            }
                        case ACObjectType.ResourceType:
                            break;
                        case ACObjectType.Privilege:
                            break;
                        default:
                            break;
                    }
                }
                this.organizations = organizations;
                this.roles = roles;
                this.groups = groups;
                this.functions = functions;
                this.menus = menus;
                this.appSystems = appSystems;
                _initialized = true;
            }
        }
    }
}
